package main

import (
	"io"
	"log"
	"net" //基本网络包
	"os"
	"strconv" //基本类型转换
)

/**
 * 服务端
 * @param int port 端口号
 */
func runServer(port int) {

	// 启动监听
	l, err := net.Listen("tcp", ":"+strconv.Itoa(port))
	if err != nil {
		log.Fatalf("服务端启动监听失败：%v", err)
	}
	log.Println("服务端已启动！")

	// 循环接受请求
	for {

		// 接受请求连接
		conn, err := l.Accept()
		if err != nil {

			// err.(net.Error)是类型断言,判断是否来自网络上的错误
			// ne.Temporary()是否是网络临时错误
			if ne, ok := err.(net.Error); !ok || !ne.Temporary() {
				log.Printf("接受请求失败：%v", err)
			}
			continue //抛弃当前请求，继续监听

		}

		log.Println("请求接受成功！")

		//*****************1、接收请求连接**********************

		// 处理远程请求连接
		go handler(conn)
	}
}

/**
 * 处理远程请求连接
 */
func handler(conn net.Conn) {
	defer conn.Close() //关闭远程链接

	//*****************1、接收客户端发送的头信息(文件名)**********************

	// 获取远程的IP地址
	rmoteAddr := conn.RemoteAddr().String()
	log.Println("远程的IP地址:", rmoteAddr)

	// 存放，获取文件头信息，例如：文件名
	p := make([]byte, 1024) //创建切片
	// n 表示实际读取到的字节数(读取网络流的字节)
	n, err := conn.Read(p)
	if err != nil {
		log.Printf("读取文件头失败（%s）%v", rmoteAddr, err)
	} else if n == 0 {
		log.Printf("空文件头（%s）", rmoteAddr)
		return
	}

	// 打印接收到的文件名
	fileName := string(p[:n])
	log.Printf("文件：%s \n\t", fileName)

	//*****************2、给客户端发送回复信息**********************

	// 回复确认信息，避免客户端过早发送，将文件内容与信息混杂一起
	conn.Write([]byte("ok"))

	//*****************3、创建一个本地文件流**********************

	// 创建文件夹
	os.MkdirAll("receive", os.ModePerm)

	// 打开一个本地文件流（创建）
	f, err := os.Create("receive/" + fileName)
	if err != nil {
		log.Printf("无法创建文件(%s):%v", rmoteAddr, err)
		return
	}
	defer f.Close() //关闭本地文件流

	//*****************4、从复制网络流到本地文件流上**********************

	// 复制网络流到本地文件流
	_, err = io.Copy(f, conn) //①文件流②网络流

	// 循环读取网络 - 这一步可以不用的，主要检证文件传输中是否有错误
	for {
		buffer := make([]byte, 1024*200) //每次读取 200 个字节
		_, err := conn.Read(buffer)      //读取网络流的字节
		if err != nil && err != io.EOF {
			log.Printf("读取失败(%s):%v", rmoteAddr, err)
		} else if err == io.EOF {
			break
		}
	}

	if err != nil {
		log.Printf("文件接受失败(%s):%v", rmoteAddr, err)
		return
	}
	log.Printf("文件接受成功(%s):%v", rmoteAddr, fileName)

}
